#ifndef CSOCKET_H
#define CSOCKET_H

#include "CommDef.h"
#include "CThreadPool.h"
#include <sys/epoll.h>
#include <sys/socket.h>
#include <pthread.h>
#include <semaphore.h>
#include <atomic>
#include <map>
#include <list>
#include <vector>

#define LISTEN_BACKLOG	511		//已完成连接队列
#define MAX_EVENT	512			//epoll_wait一次最多接收这么多个事件

typedef struct listening_s listening_t, * lp_listening_t;
typedef struct connection_s connection_t, * lp_connection_t;

class CSocekt;
typedef class CSocket CSocket;

typedef void(CSocket::* eventHandlerPt)(lp_connection_t c);	//定义成员函数指针

struct listening_s	//和监听端口有关的结构
{
	int port;		//监听的端口号
	int fd;			//套接字句柄socket
	lp_connection_t connection;	//连接池中的一个连接，注意这是个指针
};

//该结构表示一个TCP连接[客户端主动发起的、服务器被动接受的TCP连接]
struct connection_s {
	connection_s();				//构造函数
	virtual ~connection_s();	//析构函数
	void getOneToUse();			//分配出去的时候初始化一些内容
	void putOneToFree();		//回收回来的时候做一些事情

	int	fd;						//套接字句柄socket
	lp_listening_t listening;   //如果这个链接被分配了一个监听套接字，那么这个里边就监听套接字对应的那个lp_listening_t的内存首地址
	uint64_t iCurrSequence;		//每次分配出去时+1，此法也有可能再一定程度上检测错包废包
	struct sockaddr	stSockaddr;	//保存对方地址信息

	eventHandlerPt rhandler;	//读事件的相关处理方法
	eventHandlerPt whandler;	//写事件的相关处理方法

	//和epoll事件相关
	uint32_t events;			//和epoll事件相关

	//和收包相关
	unsigned char curStat;		//当前收包的状态
	char szDataHeadInfo[HEADER_BUFFSIZE];	//用于保存收到的数据的包头信息
	char* lpRecvBuf;				//接收数据的缓冲区的头指针，对接收不全的包非常有用
	unsigned int iRecvLen;		//要接收多少数据，由这个变量指定，和pRecvBuf配套使用
	char* lpRecvMemPtr;		//new出来的用于收包的内存首地址，释放用的
	pthread_mutex_t stLogicMutex;	//逻辑处理相关的互斥量

	//和发包相关
	std::atomic<int>	iSendBufferExceptionCount;	//发送消息时，如果发送缓冲区满了，则需要通过epoll事件来驱动消息的继续发送，用这个来标记发送缓冲区满
	char* lpSendMemPtr;							//发送完后释放用的，整个数据的头指针，其实是消息头+包头+包体
	char* lpSendBuffer;							//发送数据的缓冲区的头指针，其实是包头+包体
	unsigned int iSendLen;						//要发送多少数据

	time_t	lRecycleTime;						//入到资源回收站里去的时间
	time_t  lLastPingTime;						//上次ping的时间[上次发心跳包的时间]

	//和网络安全相关
	uint64_t	uLastFloodKickTime;				//flood攻击上次收到包的时间
	int			iFloodAttackCount;				//flood攻击在该时间内收到包的次数统计
	std::atomic<int> iSendCount;				//发送队列中的数据条目数，若client只发不收，则可能早成此数过大，依据此数做出踢出处理

	lp_connection_t next;						//下个连接，用于把空闲的连接对象串起来构成一个单向链表
};

//消息头，引入的目的是当收到数据包时，额外记录一些内容以备将来使用
typedef struct MsgHeader {
	lp_connection_t pConn;	//记录对应的连接，注意这是个指针
	uint64_t uCurrSequence;	//收到数据包时记录对应连接的序号，用于确定连接是否为废弃
}MsgHeader, *LPMsgHeader;


class CSocket
{
public:
	CSocket();
	virtual ~CSocket();
	virtual bool init();		//初始化函数[父进程中执行]
	virtual bool initInChild();	//初始化函数[子进程中执行]
	virtual void shutdown();	//退出函数[子进程中执行]
	void printStatisticsInfo();	//打印统计信息

	virtual void threadRecvProc(char *pMsgBuf);	//处理客户端请求，虚函数
	virtual void procPingTimeout(LPMsgHeader lpMsgHeader, time_t stCurrTime);	//心跳包检测时间到
	int epollInit();			//epoll功能初始化
	int epollProcessEvents(int nTime);	//epoll等待接收和处理事件
	int epollOperateEvent(int fd, uint32_t uEventType, uint32_t uFlag, int bcaction, lp_connection_t pConn);

protected:
	void msgSend(char *pSendBuf);		//把数据扔到待发送队列中
	void afterSocketClose(lp_connection_t pConn);	//主动关闭一个连接后要做些善后的处理函数
	size_t m_iPkgHeaderLen;	//sizeof(PkgHeader)
	size_t m_iMsgHeaderLen; //sizeof(MsgHeader)

	int m_bTimeoutKick;			//当时间到达Sock_MaxWaitTime指定的时间时，直接把客户端踢出去，只有当Sock_WaitTimeEnable = 1时，本项才有用 
	int m_iWaitTime;		//多少秒检测移除是否心跳超时

private:
	void readConf();					//专门用于读各种配置项
	bool openListeningSockets();		//监听必须的端口【支持多个端口】
	void closeListeningSockets();		//关闭监听套接字
	bool setNonBlocking(int sockfd);	//设置套接字非阻塞

	//一些业务处理函数handler
	void handleWrite(lp_connection_t pConn);	//处理写事件
	void handleAccept(lp_connection_t oldConn);	//建立新连接
	void handleRead(lp_connection_t pConn);		//处理读事件
	
	void handleClose(lp_connection_t pConn);	//处理连接关闭
	
	ssize_t recvProc(lp_connection_t pConn, char* pBuff, ssize_t iBufLen);	//接收从客户端来的数据
	void recvPkgHeaderProc(lp_connection_t pConn, bool &bFlood);	//包头收完整后的处理
	void recvPkgProc(lp_connection_t pConn, bool& bFlood);			//收到一个完整包后的处理，放到一个函数中
	void clearMsgSendQueue();										//清空发送消息队列
	ssize_t sendProc(lp_connection_t pConn, char *lpBuff, ssize_t lSize);	//将数据发送到客户端

	size_t getIPAndPort(struct sockaddr* sa, int port, u_char* ipAddr, size_t len);

	//连接池处理相关
	void initConnection();				//初始化连接池
	void clearConnection();				//回收连接池
	lp_connection_t getConnection(int sockfd);	//从连接池中获取一个空闲连接
	void freeConnection(lp_connection_t pConn);	//归还参数pConn所代表的连接到连接池中
	void addToRecycleQueue(lp_connection_t pConn);	//将要回收的连接放到一个队列中去

	//和时间相关的函数
	void addToTimerQueue(lp_connection_t pConn);	//设置踢出时钟
	time_t getEarliestTime();						//从multimap中取得最早的时间返回
	LPMsgHeader revmoveFirstTimer();				    //从multimap中移除最早的时间，并把最早的这个时间所在的项的值所对应的指针返回
	LPMsgHeader getOverTimeTimer(time_t currTime);    //根据给的当前时间，从m_timeQueueMap找到比这个时间更早的节点返回去
	void deleteFromTimerQueue(lp_connection_t pConn);	//把指定用户tcp连接从timer表中移除
	void clearAllFromTimerQueue();					//清理时间队列中所有内容

	//和网络安全相关
	bool testFlood(lp_connection_t pConn);			//测试是否为flood攻击

	//线程相关函数
	static void* sendDataThread(void* pData);	//专门用来发送数据的线程
	static void* recycleConnectionThread(void* pData);	//专门用来回收连接的线程
	static void* timerQueueMonitorThread(void* pData);	//定时器队列监视线程

	//定义线程池中线程结构
	struct ThreadItem {
		pthread_t m_tHandle;
		CSocket* m_pThis;
		bool	m_bRunning;
		//构造函数
		ThreadItem(CSocket* pSocket) : m_pThis(pSocket), m_bRunning(false) {}
		//析构函数
		~ThreadItem() {}
	};

	int m_nWorkerConnections;	//epoll连接的最大项数
	int m_nListenPortCount;		//所监听的端口数量
	int m_nEpollHandle;			//epoll_create返回的句柄

	//和连接池有关的
	std::list<lp_connection_t> m_lstConnection;		//连接列表（连接池）
	std::list<lp_connection_t> m_lstFreeConnection;	//空闲连接列表
	std::atomic<int> m_nTotalConnection;			//连接池连接总数
	std::atomic<int> m_nFreeConnection;				//连接池空闲连接数
	pthread_mutex_t m_connectionMutex;				//连接相关互斥量
	pthread_mutex_t m_recycleMutex;					//连接回收队列相关的互斥量
	std::list<lp_connection_t> m_lstRecycleConnection;	//待释放的连接列表
	std::atomic<int> m_nTotalRecycleConnection;		//待释放连接队列大小
	int m_nRecycleConnWaitTime;						//等待多少秒后才回收连接

	std::vector<lp_listening_t> m_vecListenSocket;	//监听套接字队列
	struct epoll_event	m_szEvents[MAX_EVENT];		//用于在epoll_wait()中保存返回的所发生的事件

	//消息队列
	std::list<char*> m_lstMsgSend;					//发送数据消息队列
	std::atomic<int> m_iMsgSendCount;				//发消息队列大小

	//多线程相关
	std::vector<ThreadItem*> m_vecThread;			//线程容器
	pthread_mutex_t m_msgSendMutex;					//发消息队列互斥量
	sem_t m_semMsgSend;								//发消息线程相关互斥量

	bool m_bKickEnabled;							//是否开启踢人时钟
	pthread_mutex_t m_timerMutex;					//定时器互斥量
	std::multimap<time_t, LPMsgHeader> m_mapTimer;  //时间队列
	size_t m_iTimerMapSize;							//时间队列尺寸
	time_t m_lFirstTimer;							//当前计时队列头部时间值

	//在线用户相关
	std::atomic<int> m_iOnlineUserCount;			//当前在线用户统计
	//网络安全相关
	bool m_bFloodAttackTestEnabled;						//flood攻击检测是否开启
	unsigned int m_iFloodTimeInterval;					//flood攻击检测时间间隔
	int m_nFloodKickCount;								//累积多少次踢出此客户端

	time_t m_lLastPrintTime;							//上次打印统计信息的时间
	int m_iDiscardSendPkgCount;							//丢弃的发送数据包数量
};



#endif // !CSOCKET_H
